#include "stdafx.h"
#include "Mesh.h"

#include "Renderer.h"
#include "InputLayouts.h"
#include "Material.h"
#include "LoadTexture.h"

#include "assimp.hpp"
#include "aiPostProcess.h"
#include "aiScene.h"

using namespace mgfx;
using namespace mcore;

Mesh::ResourceMap Mesh::mResources;

Mesh * Mesh::createFromFile( const std::string & fileName )
{
	MeshDesc desc;
	desc.FileName = fileName;
	return createFromFile(desc);
}

Mesh * Mesh::createFromFile(const MeshDesc & desc)
{
	ResourceMap::const_iterator found = mResources.find(desc.FileName);
	if(found != mResources.end())
	{
		return found->second;
	}
	else
	{
		std::string path = "content\\meshes\\";
		path += desc.FileName;

		MeshDesc modDesc = desc;
		modDesc.FileName = path;

		Mesh * mesh = fromFile(modDesc);
		mResources[desc.FileName] = mesh;

		return mesh;
	}
}

void Mesh::freeAll()
{
	ResourceMap::iterator itr, end = mResources.end();
	for(itr = mResources.begin() ; itr != end ; ++itr)
	{
		delete itr->second;
	}
}

Mesh * Mesh::fromFile(const MeshDesc & desc)
{
	Assimp::Importer importer;
	const aiScene* scene = importer.ReadFile(desc.FileName, aiProcessPreset_TargetRealtime_Quality);
	if(scene == NULL)
	{	
		LOG("ERROR loading mesh");
		return false;
	}

	Mesh * newMesh = new Mesh;

	int meshInd = 0;
	int numm = scene->mNumMeshes;
	//if(path == "content\\meshes\\sponza.3ds")
	//{ 
	//	meshInd = 19; numm = 20;
	//}

	for(; meshInd < numm ; meshInd++)
	{
		aiMesh * mesh = scene->mMeshes[meshInd];

		Part newPart;

		uint numVertices = mesh->mNumVertices;
		uint numIndices = mesh->mNumFaces * 3;

		Renderer * rdr = Renderer::Instance;

		mmath::BoundingSphere boundingSphere;

		std::vector<VertexPosition> positions(numVertices, VertexPosition());

		for(uint i = 0; i < mesh->mNumVertices; i++)
		{
			positions[i].Position = *(XMFLOAT3*)(&mesh->mVertices[i]);
		}

		newPart.VBPositionsOnly = rdr->addVertexBuffer(&positions[0], numVertices, sizeof(VertexPosition));

		if(desc.GenerateBoundingSphere)
			boundingSphere.construct((const byte*)&positions[0], mesh->mNumVertices, sizeof(VertexPosition));

		if(desc.PositionsOnly)
		{
			newPart.VB = newPart.VBPositionsOnly;
		}
		else
		{
			std::vector<VertexPositionNormalTangentTexture> vertices(numVertices, VertexPositionNormalTangentTexture());
			
			for(uint i = 0; i < mesh->mNumVertices; i++)
			{
				vertices[i].Position = *(XMFLOAT3*)(&mesh->mVertices[i]);
				vertices[i].Normal = *(XMFLOAT3*)(&mesh->mNormals[i]);
				vertices[i].Tangent = *(XMFLOAT3*)(&mesh->mTangents[i]);
				vertices[i].TextureCoord = *(XMFLOAT2*)(&mesh->mTextureCoords[0][i]);
			}

			newPart.VB = rdr->addVertexBuffer(&vertices[0], numVertices, sizeof(VertexPositionNormalTangentTexture));
		}

		std::vector<ushort> indices;

		for(uint i=0 ; i<mesh->mNumFaces ; ++i)
		{
			indices.push_back(mesh->mFaces[i].mIndices[0]);
			indices.push_back(mesh->mFaces[i].mIndices[1]);
			indices.push_back(mesh->mFaces[i].mIndices[2]);
		}

		newPart.IB = rdr->addIndexBuffer(&indices[0], numIndices, sizeof(short));

		newPart.NumPrimitives = mesh->mNumFaces;
		newPart.NumIndices = numIndices;
		newPart.BSphere = boundingSphere;

		newMesh->mParts.push_back(newPart);
	}

	newMesh->HasBSphere = desc.GenerateBoundingSphere;

	for (unsigned int matInd = 0 ; matInd < scene->mNumMaterials ; ++matInd)
	{
		aiMaterial * material = scene->mMaterials[matInd];

		Material * newMat = Material::create(desc.FileName + mutil::toString(matInd));

		material->Get(AI_MATKEY_COLOR_DIFFUSE, *(aiColor3D*)&newMat->DiffuseColor);
		//material->Get(AI_MATKEY_SHININESS_STRENGTH, newMat->SpecularIntensityAndPower.x);
		//material->Get(AI_MATKEY_SHININESS, newMat->SpecularIntensityAndPower.y);
		newMat->SpecularIntensityAndPower = XMFLOAT2(0.3f, 32);

		aiString textureResourceName;
		if(desc.LoadTextures && material->GetTexture(aiTextureType_DIFFUSE, 0, &textureResourceName ) == aiReturn_SUCCESS)
		{
			//newMat->DiffuseMap = loadTextureFromFile(textureResourceName.data);
		}

		if(desc.LoadTextures && material->GetTexture(aiTextureType_HEIGHT, 0, &textureResourceName ) == aiReturn_SUCCESS)
		{
			//newMat->NormalMap = loadTextureFromFile(textureResourceName.data);
		}

		newMesh->Materials.push_back(newMat);
	}

	return newMesh;
}

Mesh::~Mesh()
{
}

const std::vector<Mesh::Part> & Mesh::GetParts() const
{
	return mParts;
}
